### Copyright (C) 2010 Desmond Carter
###
### This file is part of Ar.
###
### Ar is free software: you can redistribute it and/or modify it under the
### terms of the GNU General Public License as published by the Free Software
### Foundation, either version 3 of the License, or (at your option) any later
### version.
###
### Ar is distributed in the hope that it will be useful, but WITHOUT ANY
### WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
### FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
### details.
###
### You should have received a copy of the GNU General Public License along
### with Ar. If not, see <http://www.gnu.org/licenses/>.

## This file contains the class for axes, which are basically lines and should
## probably have that as a superclass. (For later, of course.)


from ar.locals import *
import ar
import ar.drawtools as drawtools


# Eventually I want these to be iterable, but the way it currently works is
# good enough for now.
class Axis(object):
    '''Data type for axes of a coordinate plane structured to allow isometric
    projection. When creating the main board's axes, pass board_basis.'''
    def __init__(self, start=0, end=0, order=0, board_basis=3, parent=False):
        if end < start: # So that other code can assume start < end, but you
                        # can still pass the args in either order
            self.start, self.end = end, start

        else:
            self.start, self.end = start, end

        self.length = self.end - self.start

        # This arg shouldn't do anything normally, because the ArBoard class
        # already sets itself to be the parent of all its axes.
        self.parent = parent

        # This is their dimension--i.e. 2d, 3d, 4d, .... If order is equal,
        # they are parallel.
        self.order = order

        # Initialize vectorform as (order - 1) zeroes followed by length
        self.vectorform = ([0] * order)[:order] + [self.length]
        # Pad it out with zeroes
        # We have to wrap this in try...except for now because it looks for the
        # main board before finishing creating its axes. I'll have to fix that
        # somehow later.
        try:
            self.vectorform += [0] * (ar.board.basis - order)
        except AttributeError: # board does not exist
            self.vectorform += [0] * (board_basis - order)

    def __cmp__(self, axis):
        # Higher order is always greater
        if self.order != axis.order:
            return self.order - axis.order

        # If parallel, the longer is greater
        elif self.length != axis.length:
            return self.length - axis.length

        # If the length is the same, they are compared by start value
        elif self.start != axis.start:
            return self.start - axis.start

        else:
            return 0

    def __add__(self, axis):
        '''Combine them if contiguous; else ValueError.'''

        if self.order != axis.order:
            raise ValueError("These axes point in different directions.")

        # Test to see if they overlap
        if (self.end <= axis.start and self.start <= axis.end) or \
           (self.start <= axis.end and axis.start <= self.end):

            start = min((self.start, axis.start))
            end = max((self.end, axis.end))

            return Axis(start, end, order)

        else:
            raise ValueError("Axes are not contiguous.")

    def __repr__(self):
        # This is a bit Lispy, but I had no better idea. Suggestions welcome.
        return "#A({0}, {1}, {2})".format(self.start, self.end, self.order)

    def granulate(self, step=1):
        '''Divide it non-destructively into pieces of length step.'''

        answer = [Axis(x, x + step)
                  for x in xrange(self.start, self.end, step)]

        # It misbehaves in certain cases, but I think only the last entry in
        # the list is affected
        answer[-1].end = self.end

        return answer

    def contains(self, point):
        '''Note: only works with points for now.'''
        # Todo: add "is_contained" functions to every class here.

        return self.start <= point[self.order] <= self.end

    def get_point(self, coord):
        '''Get the coordinate of the main board at which you can find the given
        one-dimensional coordinate.'''

        blank = [0] * self.parent.coord_length
        blank[self.order] = coord
        if False:#len(blank) <= 2:
            print blank, "at", ar.board2screencount
        return blank

    def truncate(self, start, end):
        '''Return a copy with everything before start or after end cut off.'''

        return Axis(start, end, self.order)

    def copy(self):
        '''Return an unmodified copy.'''

        return self.truncate(self.start, self.end)

    def draw(self, origin=ar.origin, screen=ar.screen):
        '''Projects the Axis to the screen, assuming an origin at origin.'''

        drawtools.project_line(self.get_point(self.start),
                               self.get_point(self.end),
                               origin=origin, width=3, screen=screen)

    # Note: untested for step > 1. I doubt I'll ever use that anyway.
    def tickmarks(self, spans, color, origin=ar.origin,
                  screen=ar.screen, step=1):
        '''Draw tickmarks on the axis that are parallel to and the same length
        as each span.'''

        for span in spans:
            # Get start and end points of span
            start, end = map(span.get_point, (span.start, span.end))

            # Iterate through the points on self, updating start and end as we
            # go
            for point in xrange(self.start, self.end + 1, step):
                if point != 0: # Otherwise it overlaps the Axis itself.
                    start[self.order] = end[self.order] = point
                    drawtools.project_line(start, end, color=color,
                                           origin=origin, screen=screen)
